#include "sCRITextString.h"
#include "sCRIText.h"

#include <istream>
#include <iterator> 

namespace sgui
{
namespace inst
{

sTextString::sTextString(const inst::sComponentRectInstance& container, const std::wstring& content) :
	m_container(container),
	m_containerAlign(container.get<res::sComponentRectText>().getHorisontalAlign()),
	m_content(content),
	m_preset(NULL)
{
	//
}
		
const math::sRectf& sTextString::getRect() const
{
	return m_rect;
}
	
math::sRectf& sTextString::getRect()
{
	return m_rect;
}

const std::wstring& sTextString::getContent() const
{
	return m_content;
}

void sTextString::setContent(const std::wstring& content)
{
	m_content = content;

	const res::sComponentRectText& m_object = m_container.get<res::sComponentRectText>();
	const preset::sFontPreset *preset = m_object.getFontPresetGroup().get(m_container.getState());

	update(preset);
}
        
void sTextString::getCursorPosition(u32 char_, math::sPointf& res_pos, f32& res_height) const
{
    if (m_subStrings.empty())
    {
	    const engine::sFont *font = m_preset->getFont();
        math::sPointf strSize = font->getSize(getContent());

        res_height = getRect().H;
        res_pos.Y = 0;
        res_pos.X = font->getSize(getContent().substr(0, char_)).X;
    }
    else
    {
        u32 line = m_subStrings.size() - 1;

        for (u32 i = 0; i < m_subStrings.size(); i++)
        {
            if (char_ >= m_subStrings[i].m_content.size() + 1)
            {
                char_ -= m_subStrings[i].m_content.size() + 1;
            }
            else
            {
                line = i;
                break;
            }
        }

		const res::sComponentRectText& crt = m_container.get<res::sComponentRectText>();
		const float h_coef = res::FONT_ALIGN_COEF[crt.getHorisontalAlign()];  
        
	    const engine::sFont *font = m_preset->getFont();
        
        res_height = m_subStrings[line].m_rect.H;
		res_pos.X = getRect().X + font->getSize(m_subStrings[line].m_content.substr(0, char_)).X + m_subStrings[line].m_rect.X;
        res_pos.Y = getRect().Y + m_subStrings[line].m_rect.Y;
    }
}
        
u32 sTextString::getStringChar(const std::wstring& str, s32 position) const
{
    if (position < 0)
    {
        return 0;
    }

	const engine::sFont *font = m_preset->getFont();
    
    float offsetX = 0;
    wchar_t ch[2];
    ch[1] = '\0';
    for (u32 i = 0; i < str.size(); ++i)
    {
        ch[0] = str.at(i);
        float charWidth = font->getSize(ch).X;

        if (position < offsetX + charWidth / 2)
        {
            return i;
        }

        offsetX += charWidth;
    }

    return str.size();
}
        
u32 sTextString::getCharAt(const math::sPointf& point) const
{
    if (m_subStrings.empty())
    {
        return getStringChar(getContent(), point.X);
    }
    else
    {

        u32 subStringId = 0;
        if (point.Y >= 0)
        {
            subStringId = m_subStrings.size() - 1;

            for (u32 i = 0; i < m_subStrings.size(); i++)
            {
                if (point.Y < m_subStrings[i].m_rect.Y + m_subStrings[i].m_rect.H)
                {
                    subStringId = i;
                    break;
                }

            } 

            u32 prevChars = 0;

            for (u32 i = 0; i < subStringId; i++)
            {
                prevChars += m_subStrings[i].m_content.size() + 1;
            }

            return prevChars + getStringChar(m_subStrings[subStringId].m_content, point.X - m_subStrings[subStringId].m_rect.X);
        }
        return 0;
    }
}

void sTextString::update(const preset::sFontPreset *preset)
{
	if (!m_subStrings.empty())
	{
		m_subStrings.clear();
	}

	m_preset = preset;
	const engine::sFont *fnt = preset->getFont();
	
	const inst::sComponentRectInstance& crt_i = (const inst::sComponentRectInstance&)m_container;
	const res::sComponentRectText& crt = m_container.get<res::sComponentRectText>();
	if (crt.getWordWrap())
	{
        m_rect.setSize(fnt->getSize(getContent()));

		// if it does not fit the rect
		if (m_rect.W > crt_i.getRect().W)
		{
			const float space_width = fnt->getSize(L" ").X;
			const float rect_width = crt_i.getRect().W;

			std::wstringstream dataStream(getContent());
			std::vector<std::wstring> vstrings;

            std::wstring item;
            while (std::getline(dataStream, item, L' ')) {
                vstrings.push_back(item);
            }

			std::wstringstream pushStream;
			f32 pushWidth = 0;

			for (std::vector<std::wstring>::const_iterator sit = vstrings.begin(); sit != vstrings.end(); sit++)
			{
				if (pushWidth)
				{
					const math::sPointf wordSize = preset->getFont()->getSize(*sit);
					const f32 word_size = wordSize.X + space_width;
					if (pushWidth + word_size <= rect_width)
					{
						pushStream << L" " << *sit;
						pushWidth += word_size;
					}
					else
					{ 
						sTextSubString str;
						str.m_content = pushStream.str();
						str.m_rect.setSize(preset->getFont()->getSize(pushStream.str()));
						m_subStrings.push_back(str);

						pushStream.str(L"");
						pushStream.clear();
						pushStream << *sit;

						pushWidth = wordSize.X;
					}
				}
				else
				{
					pushStream << *sit;
					pushWidth = preset->getFont()->getSize(*sit).X;
				}
			}

			sTextSubString str;
			str.m_content = pushStream.str();
			str.m_rect.setSize(preset->getFont()->getSize(pushStream.str()));
			m_subStrings.push_back(str);

            m_rect.setSize(math::sPointf(0, 0));
			for (std::deque<sTextSubString>::iterator it = m_subStrings.begin(); it != m_subStrings.end(); it++)
			{
				if (m_rect.W < (*it).m_rect.W) m_rect.W = (*it).m_rect.W;
				(*it).m_rect.Y = m_rect.H;
				m_rect.H += (*it).m_rect.H;
			}
			
			const res::sComponentRectText& crt = m_container.get<res::sComponentRectText>();
			const float h_coef = res::FONT_ALIGN_COEF[crt.getHorisontalAlign()];

			for (std::deque<sTextSubString>::iterator it = m_subStrings.begin(); it != m_subStrings.end(); it++)
			{
				(*it).m_rect.X = m_rect.W * h_coef - (*it).m_rect.W * h_coef;
			}
		}
	}
	else
	{
        m_rect.setSize(fnt->getSize(getContent()));
	}
}
		
void sTextString::draw(engine::sDrawStack& drawStack) const
{
	if (m_preset)
	{
		const engine::sFont *fnt = m_preset->getFont();

		if (m_subStrings.empty())
		{
			fnt->draw(*drawStack.top(), getContent(), m_preset->getColor());
		}
		else
		{
			for (std::deque<sTextSubString>::const_iterator it = m_subStrings.begin(); it != m_subStrings.end(); it++)
			{
				obj::sTransform t;
				t.getPosition().set((*it).m_rect.getPosition());
				drawStack.top()->pushTransform(t);
				fnt->draw(*drawStack.top(), (*it).m_content, m_preset->getColor());
				drawStack.top()->popTransform();
			}
		}
	}
}

};
};